package lex.lianliankan;

import java.awt.Point;
import java.util.Random;

import javax.swing.event.EventListenerList;

import lex.lianliankan.utils.Images;

public class GameModel implements LianLianKanModel {
	private int rowCount = 7;
	private int columnCount = 12;
	private int keyCount = 20;
	/**
	 * table�����ܱ�һȦ"�հ�"��Ԫ���Χ����Щ�հ׵�Ԫ�ǲ��ɼ�ģ�ֵΪ0.<br>
	 * һ��2��x3�еı������ͼ��(ע�⵽'0'��ʾ�հ�)
	 * 
	 * <pre>
	 * 0 0 0 0 0
	 * 0 1 0 1 0
	 * 0 2 2 0 0
	 * 0 0 0 0 0
	 * </pre>
	 */
	private int table[][] = null;
	// event support
	private EventListenerList listenerList = null;

	public GameModel() {
		this(8, 12, Images.getKeyCount());
	}

	public GameModel(int rowCount, int columnCount, int keyCount) {
		rowCount += 2;
		columnCount += 2;
		this.rowCount = rowCount;
		this.columnCount = columnCount;
		this.table = new int[rowCount][columnCount];
		for (int row = 0; row < rowCount; row++)
			for (int column = 0; column < columnCount; column++)
				table[row][column] = 0;
		this.keyCount = keyCount;
		this.listenerList = new EventListenerList();
	}

	public int getRowCount() {
		return rowCount - 2;
	}

	public int getColumnCount() {
		return columnCount - 2;
	}

	public void setKey(int key, int row, int column) {
		table[row + 1][column + 1] = key;
		this.fireCellChanged(row, column);
	}

	public int getKey(int row, int column) {
		return table[row + 1][column + 1];
	}

	public boolean isFinished() {
		for (int row = 0; row < rowCount; row++) {
			for (int col = 0; col < columnCount; col++)
				if (table[row][col] != 0)
					return false;
		}
		return true;
	}

	/**
	 * ����ϴ��
	 */
	public void shuffle() {
		Random rand = new Random();
		for (int row = 1; row < rowCount - 1; row++) {
			for (int column = 1; column < columnCount - 1; column++) {
				int row2 = rand.nextInt(rowCount - 2) + 1;
				int column2 = rand.nextInt(columnCount - 2) + 1;
				int tmp = table[row][column];
				table[row][column] = table[row2][column2];
				table[row2][column2] = tmp;
				this.fireCellChanged(row - 1, column - 1);
				this.fireCellChanged(row2 - 1, column2 - 1);
			}
		}
	}

	public void create() {
		Random rand = new Random();
		int buffer[] = new int[keyCount];
		java.util.Arrays.fill(buffer, 3);
		for (int row = 0; row < rowCount; row++) {
			for (int column = 0; column < columnCount; column++) {
				if (row == 0 || column == 0 || row == rowCount - 1 || column == columnCount - 1) {
					table[row][column] = 0;
					continue;
				}
				if (column % 2 != 1)
					continue;
				int key = 0;
				do
					key = rand.nextInt(this.keyCount) + 1;
				while (buffer[key - 1] <= 0);
				buffer[key - 1]--;
				table[row][column] = key;
				table[row][(column + 1)] = key;
			}
		}
		for (int i = 0; i < 10; i++)
			this.shuffle();
	}

	/**
	 * Ѱ���Ƿ񻹴���һ��������ȥ��·��
	 * 
	 * @return
	 */
	public Point[] findPath() {
		StringBuilder buf = new StringBuilder("found: ( ");
		for (int row = 0; row < rowCount; row++) {
			for (int col = 0; col < columnCount; col++) {
				Point first = new Point(row, col);
				for (int row2 = 0; row2 < rowCount; row2++) {
					for (int col2 = 0; col2 < columnCount; col2++) {
						Point second = new Point(row2, col2);
						if (table[first.x][first.y] != 0 && table[second.x][second.y] != 0 && canRemove(first.x, first.y, second.x, second.y)) {
							buf.append(first.y).append(", ").append(first.x);
							buf.append(" ) <-> ( ").append(second.y);
							buf.append(", ").append(second.x).append(" )");
							System.out.println(buf.toString());
							Point twoPoints[] = new Point[2];
							twoPoints[0] = first;
							twoPoints[1] = second;
							return twoPoints;
						}
					}
				}
			}
		}
		return null;
	}

	public boolean canRemove(int row1, int column1, int row2, int column2) {
		if (row1 == row2 && column1 == column2)
			return false;
		if (table[row1][column1] != table[row2][column2])
			return false;
		int key1 = table[row1][column1];
		int key2 = table[row2][column2];
		table[row1][column1] = 0;
		table[row2][column2] = 0;
		for (int i = 0; i < rowCount; i++)
			if (rowOp(i, column1, column2) && colOp(column1, i, row1) && colOp(column2, i, row2)) {
				table[row1][column1] = key1;
				table[row2][column2] = key2;
				return true;
			}
		for (int i = 0; i < columnCount; i++)
			if (colOp(i, row1, row2) && rowOp(row1, i, column1) && rowOp(row2, i, column2)) {
				table[row1][column1] = key1;
				table[row2][column2] = key2;
				return true;
			}
		table[row1][column1] = key1;
		table[row2][column2] = key2;
		return false;
	}

	private boolean colOp(int column, int minRow, int maxRow) {
		int min = Math.min(minRow, maxRow);
		int max = Math.max(minRow, maxRow);
		for (int row = min; row <= max; row++)
			if (table[row][column] != 0)
				return false;
		return true;
	}

	private boolean rowOp(int row, int minColumn, int maxColumn) {
		int min = Math.min(minColumn, maxColumn);
		int max = Math.max(minColumn, maxColumn);
		for (int col = min; col <= max; col++)
			if (table[row][col] != 0)
				return false;
		return true;
	}

	public void addLianLianKanListener(LianLianKanListener l) {
		this.listenerList.add(LianLianKanListener.class, l);
	}

	public void removeLianLianKanListener(LianLianKanListener l) {
		this.listenerList.remove(LianLianKanListener.class, l);
	}

	protected void fireCellChanged(int row, int column) {
		Object[] listeners = listenerList.getListenerList();
		for (int i = listeners.length - 2; i >= 0; i -= 2)
			if (listeners[i] == LianLianKanListener.class)
				((LianLianKanListener) listeners[i + 1]).cellChanged(row, column);
	}
}
